You have an custom developed Web app or Web API using Asp.Net or Asp.Net Core and you want to control which issuers have access to your app. There are a couple ways to do this.

Use Multiple Authentication schemes

One way to do this and is probably the most recommended way is to perform what is documented here…

https://github.com/AzureAD/microsoft-identity-web/wiki/multiple-authentication-schemes

In this solution, you’ll want to have different Web App or API endpoints for your different users based on the authentication scheme to be used. Otherwise meaning having different entry points.

We have another blog that talk more about how to implement multiple authentication schemes for B2C apps that need to handle multiple custom B2C policies…

https://blogs.aaddevsup.xyz/2022/10/how-to-resolve-idx10501-errors-in-a-b2c-microsoft-identity-web-application/

Simple Multiple Issuer validation

If you really want to stick to one entry point, here is another option.

If you have a simple scenario where the Identity Provider is Azure Active Directory and the same signing keys can be used, you can simply add a list of Issuers you can validate… (Keep in mind each tenant in Azure AD will have a different issuer value)

# Add AAD JWT Bearer Authentication
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);

builder.Services.Configure<JwtBearerOptions>(options =>
{ 
  var tvp = new TokenValidationParameters
  {
    ValidIssuers =  builder.Configuration.GetSection("AzureAd:ValidIssuers").Get<List<string>>(),
  };

  options.TokenValidationParameters = tvp;
});

Your appsettings.json should look something like this…

{
  "AzureAd": {
    //... 
    // Other Settings
    //...

    "ValidIssuers": [
      // Validate AAD v2 tokens
      "https://login.microsoftonline.com/{TENANT_1}/v2.0/", 
      "https://login.microsoftonline.com/{TENANT_2}/v2.0/" 
      // Validate AAD v1 tokens
      "https://sts.windows.net/{TENANT_1}/", 
      "https://sts.windows.net/{TENANT_2}/"
    ]
}

Complex Multiple Issuer Validation (and signature validation)

When you want to validate multiple tokens from different issuers and the signing keys are different, this gets tricky. Now you have to consider how are you going to discover the signing keys.

The following code snippet is just an example of custom logic you could build…

builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{ 
    // IdentityModelEventSource.ShowPII is used for debugging
    IdentityModelEventSource.ShowPII = true;

    var tvp = new TokenValidationParameters
    {
        # Event handler for deciding which signing keys to use
        IssuerSigningKeyResolver = (s, token, kid, tvp) => {
            IConfigurationManager<OpenIdConnectConfiguration> configManager = null!;

            // Support AAD/B2C
            if (token.Issuer.Contains("login.microsoftonline.com") || token.Issuer.Contains("sts.windows.net") || token.Issuer.Contains("b2clogin.com"))
            {
                configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{token.Issuer}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
            }

            // Support other IdP
            else
            {
                configManager = new ConfigurationManager<OpenIdConnectConfiguration>(builder.Configuration["OtherIdp:Metadata"], new OpenIdConnectConfigurationRetriever());
            }

            OpenIdConnectConfiguration config = configManager.GetConfigurationAsync(System.Threading.CancellationToken.None).GetAwaiter().GetResult();
            return config.SigningKeys;
        },
        ValidAudiences =  builder.Configuration.GetSection("OtherIdp:ValidAudiences").Get<List<string>>(),
        ValidIssuers = builder.Configuration.GetSection("OtherIdp:ValidIssuers").Get<List<string>>(),
    };

Multiple Audience Validation

If your tokens are coming with different “aud” claim, you can add additional Audience validations…

builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAd");

builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{ 
  //...
  // Other token validation parameters
  //...

  var tvp = new TokenValidationParameters
  {
    //...
    // Other token validation parameters
    //...

    ValidAudiences =  builder.Configuration.GetSection("AzureAd:ValidAudiences").Get<List<string>>(),
  };

  options.TokenValidationParameters = tvp;

Your appsettings.json should look something like this…

{
  "AzureAd": {
    //... 
    // Other Settings
    //...

    "ValidAudiences": [
      "api://c60eeff9-1329-4ddb-ba4a-7e6555391c4d",
      "c60eeff9-1329-4ddb-ba4a-7e6555391c4d",
      "api://e92f9573-6f44-46b2-a4a9-1936d5f16ab5",
      "e92f9573-6f44-46b2-a4a9-1936d5f16ab5",
    ]
}

Leave a Comment